# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from abc import ABCMeta, abstractmethod
from hysop.tools.htypes import check_instance, first_not_None, to_tuple
from hysop.tools.numpywrappers import npw
[docs]
class SymbolContainerI(metaclass=ABCMeta):
def _get_symbol(self):
"""
Return a Symbol that can be used to compute symbolic expressions
referring to this continuous field.
"""
assert hasattr(self, "_symbol"), "Symbol has not been defined."
return self._symbol
symbol = property(_get_symbol)
s = property(_get_symbol)
[docs]
class NamedObjectI(metaclass=ABCMeta):
[docs]
def __new__(cls, name, pretty_name=None, latex_name=None, var_name=None, **kwds):
"""
Create an abstract named object that contains a symbolic value.
name : string
A name for the field.
pretty_name: string, optional.
A pretty name used for display whenever possible.
Defaults to name.
kwds: dict
Keywords arguments for base class.
"""
obj = super().__new__(cls, **kwds)
obj.rename(
name=name, pretty_name=pretty_name, latex_name=latex_name, var_name=var_name
)
return obj
def __init__(self, name, pretty_name=None, latex_name=None, var_name=None, **kwds):
super().__init__(**kwds)
[docs]
def rename(self, name, pretty_name=None, latex_name=None, var_name=None):
"""Change the names of this object."""
check_instance(name, str)
check_instance(pretty_name, str, allow_none=True)
check_instance(latex_name, str, allow_none=True)
pretty_name = first_not_None(pretty_name, name)
latex_name = first_not_None(latex_name, name)
check_instance(pretty_name, str)
self._name = name
self._pretty_name = pretty_name
self._latex_name = latex_name
def _get_name(self):
"""Return the name of this field."""
return self._name
def _get_pretty_name(self):
"""Return the pretty name of this field."""
return self._pretty_name
def _get_latex_name(self):
"""Return the latex name of this field."""
return self._latex_name
def __str__(self):
return self.long_description()
[docs]
@abstractmethod
def short_description(self):
"""Short description of this field as a string."""
pass
[docs]
@abstractmethod
def long_description(self):
"""Long description of this field as a string."""
pass
name = property(_get_name)
pretty_name = property(_get_pretty_name)
latex_name = property(_get_latex_name)
[docs]
class NamedScalarContainerI(NamedObjectI, SymbolContainerI):
@property
def ndim(self):
"""Number of dimensions of this this tensor."""
return 0
def _get_var_name(self):
"""Return the variable name of this field."""
return self._var_name
[docs]
def rename(self, name, pretty_name=None, latex_name=None, var_name=None):
"""Change the names of this object."""
super().rename(name=name, pretty_name=pretty_name, latex_name=latex_name)
self.check_and_set_varname(first_not_None(var_name, self._name))
[docs]
def check_and_set_varname(self, var_name):
check_instance(var_name, str, allow_none=True)
msg = f"Invalid variable name {var_name}."
if var_name[0] in tuple(str(x) for x in range(10)):
raise RuntimeError(msg)
for c in "/*+-=|&()[]{}-!?:;,'\"#$^%<>@":
if c in var_name:
raise RuntimeError(msg)
self._var_name = var_name
[docs]
def nd_iter(self):
"""Return an nd-indexed iterator of contained objects."""
yield ((1,), self)
[docs]
def __iter__(self):
"""Return an iterator on unique scalar objects."""
return (self,).__iter__()
[docs]
def __tuple__(self):
"""
Fix hysop.tools/type.to_tuple for FieldContainers,
because __iter__ has been redefined.
"""
return (self,)
[docs]
def __contains__(self, obj):
"""Check if a scalar object is contained in self."""
return obj is self
def __getitem__(self, slc):
return self
var_name = property(_get_var_name)
[docs]
class NamedTensorContainerI(NamedObjectI, SymbolContainerI):
def __new__(cls, contained_objects, **kwds):
check_instance(contained_objects, npw.ndarray)
obj = super().__new__(cls, **kwds)
obj._contained_objects = contained_objects
return obj
def __init__(self, contained_objects, **kwds):
super().__init__(**kwds)
[docs]
def rename(self, name, pretty_name=None, latex_name=None, var_name=None):
"""Change the names of this object."""
assert var_name is None, "Tensor do not have variable names."
super().rename(name=name, pretty_name=pretty_name, latex_name=latex_name)
@property
def size(self):
"""Full size of this container as if it was a 1D tensor."""
return self._contained_objects.size
@property
def shape(self):
"""Shape of this tensor."""
return self._contained_objects.shape
@property
def ndim(self):
"""Number of dimensions of this this tensor."""
return self._contained_objects.ndim
[docs]
def new_empty_array(self, dtype=object):
"""Return a new empty array of the same shape as self."""
if dtype is object:
array = npw.empty(shape=self.shape, dtype=dtype)
array[...] = None
else:
array = npw.zeros(shape=self.shape, dtype=dtype)
return array
[docs]
def iter_fields(self):
"""Return an iterator on unique scalar object along with 1d index."""
yield from enumerate(self._contained_objects.ravel())
[docs]
def nd_iter(self):
"""Return an nd-indexed iterator of contained objects."""
for idx in npw.ndindex(*self._contained_objects.shape):
yield (idx, self._contained_objects[idx])
[docs]
def __iter__(self):
"""Return an iterator on unique scalar objects."""
return self._contained_objects.ravel().__iter__()
[docs]
def __tuple__(self):
"""
Fix hysop.tools/type.to_tuple for FieldContainers,
because __iter__ has been redefined.
"""
return (self,)
[docs]
def __contains__(self, obj):
"""Check if a scalar object is contained in self."""
return obj in self._contained_objects
@abstractmethod
def __getitem__(self, slc):
pass